using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;

namespace EasyPak
{
	/// <summary>
	/// A content manager used to read files stored inside of an EasyPak pak file.
	/// </summary>
	public class PakContentManager : ContentManager
	{
		string pakFilePath;
		PakFile pakFile;

		/// <summary>
		/// Gets the pak file's path.
		/// </summary>
		public string PakFilePath
		{
			get { return pakFilePath; }
		}

		/// <summary>
        /// Creates a new PakContentManager that loads files from the specified
		/// pak file.
		/// </summary>
		/// <param name="serviceProvider">Service provider</param>
		/// <param name="pakFile">Pak file to extract assets from</param>
        public PakContentManager(IServiceProvider serviceProvider, string pakFilePath)
			: base(serviceProvider)
		{
            this.pakFile = PakFile.Read(pakFilePath);
            this.pakFilePath = pakFilePath;
		}

        /// <summary>
        /// 
        /// </summary>
        public void ReleasePakFile()
        {
            this.pakFile = null;
        }

		protected override Stream OpenStream(string assetName)
		{
            if (pakFile == null)
                throw new NullReferenceException(string.Format("Attempting to call OpenStream(\"{0}\") when pak file is released or not yet loaded", assetName));

            PakEntry pe = pakFile[assetName + ".xnb"];
            if (pe == null)
                throw new Exception("Failed to find asset '" + assetName + "' in pak file.");

            return pe.GetStream();
		}

		/// <summary>
		/// Gets an array of the asset names of the content inside the pak file.
		/// </summary>
		/// <returns>An array of strings with all the asset names.</returns>
		public string[] GetAssetNames()
		{
            if (pakFile == null)
                throw new NullReferenceException("Attempting to call GetAssetNames() when pak file is released or not yet loaded");

			List<string> filenames = new List<string>();

			foreach (PakEntry entry in pakFile)
			{
				string name = entry.FileName;
				if (name.EndsWith(".xnb"))
					name = name.Remove(name.Length - 4, 3);
				filenames.Add(name);
			}

			return filenames.ToArray();
		}

		/// <summary>
		/// Gets an array of asset names from a specific directory within
		/// the pak file.
		/// </summary>
		/// <param name="directory">The directory to get asset names from</param>
		/// <returns>An array of strings with all the asset names</returns>
		public string[] GetAssetNamesFromDirectory(string directory)
		{
            if (pakFile == null)
                throw new NullReferenceException(string.Format("Attempting to call GetAssetNamesFromDirectory(\"{0}\") when pak file is released or not yet loaded", directory));

			List<string> filenames = new List<string>();

			foreach (PakEntry entry in pakFile)
			{
				string name = entry.FileName;
				if (name.EndsWith(".xnb"))
					name = name.Remove(name.Length - 4, 3);

				string[] parts = name.Split('\\');
				string dir = "";
				for (int i = 0; i < parts.Length - 1; i++)
					dir += parts[i] + "/";
				if (dir == directory)
					filenames.Add(name);
			}

			return filenames.ToArray();
		}

		/// <summary>
		/// Gets a Stream for a file inside of the pak file. For use with non-Content Pipeline
		/// files such as plain images or XML files.
		/// </summary>
		/// <param name="filename">The name of the file to find (with extension)</param>
		/// <returns>A Stream that can be used to load the file</returns>
		public Stream GetFileStream(string filename)
		{
            if (pakFile == null)
                throw new NullReferenceException(string.Format("Attempting to call GetFileStream(\"{0}\") when pak file is released or not yet loaded", filename));

            PakEntry pe = pakFile[filename];
            if (pe == null)
                throw new Exception("Failed to find asset '" + filename + "' in pak file.");

            return pe.GetStream();
		}
	}
}
